package com.calwen.xsmarthome.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.MD5;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.calwen.xsmarthome.bo.DeviceProperty;
import com.calwen.xsmarthome.service.MjService;
import com.calwen.xsmarthome.util.MjUtil;
import com.calwen.xsmarthome.util.OkHttpUtil;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Cookie;
import okhttp3.HttpUrl;
import okhttp3.Response;
import org.springframework.stereotype.Service;

import java.net.URLEncoder;
import java.security.MessageDigest;
import java.util.*;

/**
 * 米家服务
 * 获取设备ID "/home/device_list" <br>
 * 设置属性uri：/miotspec/prop/set <br>
 * 获取属性uri：/miotspec/prop/get <br>
 * 使用方法uri：/miotspec/action <br>
 */
@Slf4j
@Service
public class MjServiceImpl implements MjService {


    private Map<String, Object> authMap;

    @Override
    public boolean setProperty(String id, List<DeviceProperty> o) {
        Map<String, Object> map = new HashMap<>();
        map.put("params", o);
        Object result = post("/miotspec/prop/set", map);
        return ObjectUtil.isNotNull(result);
    }

    @Override
    public Object getProperty(Object o) {
        return post("/miotspec/prop/get", o);
    }


    @Override
    public boolean doAction(Object o) {
        Object result = post("/miotspec/action", o);
        return ObjectUtil.isNotNull(result);
    }

    @Override
    public Object getHomeDeviceList(Object o) {
        Object res = post("/home/device_list", o);
        return res;
    }

    /**
     * 发送 API 请求
     */
    public Object post(String url, Object data) {
        String dataStr = JSON.toJSONString(data);
        if (authMap == null) {
            authMap = getAuth("17607889421", "WHL531222");
        }
        String serviceToken = String.valueOf(authMap.get("serviceToken"));
        String securityToken = String.valueOf(authMap.get("securityToken"));
        String deviceId = String.valueOf(authMap.get("deviceId"));
        String userId = String.valueOf(authMap.get("userId"));
        if (serviceToken == null) {
            log.error("请先登录");
            return null;
        }

        try {
            Map<String, Object> header = MjUtil.getMobileRequestHeader();
            header.put("x-xiaomi-protocal-flag-cli", "PROTOCAL-HTTP2");
            header.put("Cookie", "PassportDeviceId=" + deviceId + ";userId=" + userId + ";serviceToken=" + serviceToken + ";");

            String nonce = RandomUtil.randomString(16);
            String signedNonce = MjUtil.generateSignedNonce(securityToken, nonce);
            String signature = MjUtil.generateSignature(url, signedNonce, nonce, dataStr);

            Map<String, Object> param = new HashMap<>();
            param.put("_nonce", nonce);
            param.put("data", dataStr);
            param.put("signature", signature);

            url = "https://api.io.mi.com/app" + url;
            log.info("[MjServiceImpl post] url={},请求数据={}", url, JSON.toJSONString(param));
            String response = OkHttpUtil.doPostFormBody(url, param, header);
            log.info("[MjServiceImpl post]  url={},响应数据={}", url, response);
            JSONObject jsonObject = JSONObject.parseObject(response);
            if (jsonObject == null || !Objects.equals(jsonObject.getInteger("code"), 0)) {
                return null;
            }
            return jsonObject.get("result");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 登录
     */
    public Map<String, Object> getAuth(String userName, String password) {
        JSONObject data = new JSONObject();
        Map<String, Object> headerMap = MjUtil.getMobileRequestHeader();

        // 1.获取登录参数
        String serviceLoginUrl = "https://account.xiaomi.com/pass/serviceLogin?sid=xiaomiio&_json=true";
        String response = OkHttpUtil.doGet(serviceLoginUrl, null, headerMap);
        JSONObject result = JSONObject.parseObject(response.substring(11));

        // 2.账号密码登录
        Map<String, Object> param = new HashMap<>();
        param.put("qs", result.getString("qs"));
        param.put("sid", result.getString("sid"));
        param.put("_sign", result.getString("_sign"));
        param.put("callback", result.getString("callback"));
        param.put("user", userName);
        param.put("hash", MD5.create().digestHex(password).toUpperCase());
        param.put("_json", "true");
        String serviceLoginAuth2Url = "https://account.xiaomi.com/pass/serviceLoginAuth2";
        response = OkHttpUtil.doPostFormBody(serviceLoginAuth2Url, param, headerMap);
        result = JSONObject.parseObject(response.substring(11));
        if (result.getIntValue("code") != 0) {
            data.put("code", result.getIntValue("code"));
            data.put("message", result.getString("desc"));
            return data;
        }

        // 3.获取service token
        try {
            String nonce = result.getString("nonce");
            String location = result.getString("location");
            String userId = result.getString("userId");
            String securityToken = result.getString("ssecurity");
            String n = String.format("nonce=%s&%s", nonce, securityToken);
            MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
            messageDigest.update(n.getBytes("utf-8"));
            String url = location + "&clientSign=" + URLEncoder.encode(Base64.getEncoder().encodeToString(messageDigest.digest()), "utf-8");
            try (Response responseData = OkHttpUtil.doGetResponse(url, null, headerMap)) {
                List<Cookie> cookies = Cookie.parseAll(HttpUrl.parse(url), responseData.headers());
                for (Cookie cookie : cookies) {
                    data.put(cookie.name(), cookie.value());
                }
            }
            data.put("userId", userId);
            data.put("securityToken", securityToken);
            data.put("deviceId", RandomUtil.randomString(16));
            data.put("code", 0);
            data.put("message", "登录成功");
            return data;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}
